home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / newsgrp / group01b.txt / 000053_icon-group-sender_Wed Mar 7 16:27:05 2001.msg < prev    next >
Internet Message Format  |  2002-01-03  |  12KB

  1. Return-Path: <icon-group-sender>
  2. Received: (from root@localhost)
  3.     by baskerville.CS.Arizona.EDU (8.11.1/8.11.1) id f27NQsQ29271
  4.     for icon-group-addresses; Wed, 7 Mar 2001 16:26:54 -0700 (MST)
  5. Message-Id: <200103072326.f27NQsQ29271@baskerville.CS.Arizona.EDU>
  6. Date: Wed, 07 Mar 2001 15:17:25 -0600
  7. From: gep2@terabites.com
  8. Subject: Re: New Scientist puzzle
  9. To: icon-group@cs.arizona.edu
  10. Errors-To: icon-group-errors@cs.arizona.edu
  11. Status: RO
  12. Content-Length: 10857
  13.  
  14. > It is interesting to look at the solution strategies followed. Unfortunately
  15. I can't read (most) of them... It would be nice if there were explanations of
  16. the diverse approaches for non-multilinguists to read.
  17.  
  18. > For instance, APL has a 5 line solution, but seems to use a monstrous
  19. array on which it does pattern matching? I could (and would like to)
  20. learn a great deal by implementing such strategies in other languages.
  21.  
  22. OK... a couple of days ago I posted a more detailed look at and explanation of 
  23. my SPITBOL solution to the SNOBOL4 list... I'll repost it here.  (It was a reply 
  24. to a post giving another alternative solution in generic SNOBOL4).
  25.  
  26. <---- Begin Forwarded Message ---->
  27. Return-Path: <snobol4@mercury.dsu.edu>
  28. Date: Mon, 5 Mar 2001 02:00:51 -0600 (CST)
  29. Reply-To: snobol4@mercury.dsu.edu
  30. From: gep2@terabites.com
  31. To: Multiple recipients of list <snobol4@mercury.dsu.edu>
  32. Subject: Re: New Scientist puzzle
  33.  
  34. >> Here, as promised, is my original solution in SPITBOL:
  35. >
  36. >
  37. >      sq = 31
  38. >      sqp1 = fence breakx("x") ("x" (len(1) $ n *notany(n) $ e
  39. > +       *notany(n e) $ u *n) $ n2) $ xn2
  40. > +     *?(sqs ? fence breakx("x") ("x" (*notany(n e u) $ v
  41. > +       *notany(n e u v) $ i e *notany(n e u v i) $ r) $ n1) $ xn1
  42. > +       *?(solbuf = solbuf "X" n1 xn2)
  43. > +       *?($xn1 = $xn1 + 1) *?($xn2 = $xn2 + 1) fail)
  44. >      lp = fence breakx("X") ("X" len(4) $ n1) $ xn1 ("x" len(4) $ n2) $ xn2
  45. > +       *eq($xn1,1) *eq($xn2,1) *?(output = n1 "," n2) fail
  46. > sqsfill    sqs = ((sqs "x" (((sq = sq + 1) le(sq,99)) * sq)),
  47. > +     (sqs ? sqp1), (solbuf ? lp))            :s(sqsfill)
  48. > end
  49. >
  50. > and the output from the last run:
  51. >
  52. > [quote]
  53. >
  54. > 6241,9409
  55.  
  56. > Not knowing anything about the spitbol extensions I find your solution hard  
  57. to follow. 
  58.  
  59. Actually, it's pretty simple... there's only one goto and one label (other than 
  60. the END) in the whole program.
  61.  
  62. There are three SPITBOL extensions:
  63.  
  64.   1)  Embedded pattern match.   (subject ? pattern)  which returns (on success) 
  65. the portion of the subject matched by the pattern.  You can also use replacement 
  66. like any normal S*BOL pattern match, in which case the returned value is the 
  67. subject after the replacement (or maybe just the new value of the replaced 
  68. portion?  I'm not really sure as I'm writing this...)
  69.  
  70.   2)  Embedded assignment.  (subject = object)  which returns the value of the 
  71. subject.  This is very, very useful for creating side effects during a pattern 
  72. match (including generating debug output, for instance).
  73.  
  74.   3)  Alternative evaluation.  (expr1, expr2, expr3, ...)  which evaluates each 
  75. of the expressions in turn, and returns the value of the first one which 
  76. succeeds.  Subsequent expressions are not evaluated.
  77.  
  78. > Isn't it awfully complicated for such a simple problem? 
  79.  
  80. Actually, no, it's really pretty simple.
  81.  
  82. One really nice thing about doing stuff like this is that you're able to use the 
  83. pattern matcher to do recursion and iteration, often without having to resort to 
  84. explicit labels and loop counters.
  85.  
  86. One of the things that I did in my solution was to precompute all the squares, 
  87. so that no multiplication (indeed, no arithmetic at all other than the tallying 
  88. the solutions) is necessary during the final portion of the program.  (Your 
  89. solution involves computing all the squares repeatedly for every qualifying 
  90. value of nn).
  91.  
  92. One of the other things I did was to prebuild the patterns, which is something 
  93. I'm relatively in the habit of doing even though the efficiency gain (in this 
  94. program at least) is certainly negligible, both because of all the unevaluated 
  95. expressions in the pattern, and since each of the patterns is only invoked ONE 
  96. time anyhow!  It does help clarify the program, though, because the structure of 
  97. the alternative evaluation expression at sqsfill is more apparent.
  98.  
  99. I qualified the "neun" term first, since that one has only three varying digits 
  100. and that (probably) truncates more of the possible solutions earlier in the 
  101. computation.
  102.  
  103. One thing which I did that apparently wasn't necessary was to make sure that the 
  104. indirection used an alpha character at the beginning of the name, thus following 
  105. normal variable naming conventions.  The fact that your solution works proves 
  106. that it isn't necessary to do that.
  107.  
  108. Also my solution drops potential candidates out of further consideration earlier 
  109. than yours does, since you break out solutions into all four of the component 
  110. characters even when the second one might already disqualify the candidate.
  111.  
  112. One minor problem with your solution is that if there WERE another solution pair 
  113. where both neun and vier values were unique. your program would only print the 
  114. first one.  It's pretty simple to fix that, though.
  115.  
  116. Your solution would run faster if you'd use *differ(a,b) instead of *ne(a,b) 
  117. since ne() forces each string [OK, character] to be converted to integer before 
  118. the comparison can be done.
  119.  
  120. In my programs, I tend to use stuff like FENCE BREAKX("X") "X" instead of just 
  121. "X" (which is running in unanchored mode) because I believe it's usually faster 
  122. to use BREAKX than it is to have the pattern matcher abandon the position and 
  123. step down the string to try again.  (Admittedly I haven't benchmarked that in 
  124. quite a long time).
  125.  
  126. Here's a running commentary...:
  127.  
  128.     sq = 31
  129.  
  130. Merely initalizes the loop counter.  Would sure like to have eliminated that, 
  131. but it would add more complexity elsewhere.
  132.  
  133. The following pattern is where most of the magic happens.  When it's running, 
  134. the string SQS consists of the four-digit squares, separated by "x" characters.
  135.  
  136. The pattern matches each of the four-digit squares in turn, starting at the 
  137. beginning of the string, and looks for one matching the "neun" rule.  When it 
  138. finds such, it then fires off an embedded pattern match, starting again at the 
  139. beginning of the list of squares, to find a square matching the "vier" rule.
  140. If it finds a pair that work, it tacks them onto the end of the solution buffer 
  141. SOLBUF (in the form "Xvierxneun") and tallies the number of times each square is 
  142. used via indirection.  The "fail" forces the next vier to be found, if there is 
  143. one, and ultimately forces the embedded pattern match to fail, which forces the 
  144. outer pattern match to resume looking for another neun square.  Ultimately, 
  145. however, the sqp1 pattern is still doomed to fail because the embedded pattern 
  146. match looking for the vier term cannot succeed.  But that's okay, because 
  147. meanwhile it's built the solution buffer which contains all the candidate pairs.
  148.  
  149. >      sqp1 = fence breakx("x") ("x" (len(1) $ n *notany(n) $ e
  150. > +       *notany(n e) $ u *n) $ n2) $ xn2
  151. > +     *?(sqs ? fence breakx("x") ("x" (*notany(n e u) $ v
  152. > +       *notany(n e u v) $ i e *notany(n e u v i) $ r) $ n1) $ xn1
  153. > +       *?(solbuf = solbuf "X" n1 xn2)
  154. > +       *?($xn1 = $xn1 + 1) *?($xn2 = $xn2 + 1) fail)
  155.  
  156. The next pattern processes the solution buffer.  It looks for each "X", then 
  157. four characters, then "x", four more characters.  Here I'm actually guilty of 
  158. the same thing I chastised you about;  I really should have moved the 
  159. *eq($xn1,1) before the matching of "x" etc, which would have allowed the matcher 
  160. to step to the next solution pair sooner.  Note that here I'm using the fact 
  161. that variable names are not case-sensitive (which I thought was really cute 
  162. while I was doing it, although now that I'm looking at it I'm wondering if 
  163. that's true when using indirection).  Anyhow, when it finds a solution pair of 
  164. squares which are both unique, it outputs the pair and then again hits a fail, 
  165. which forces the pattern matcher to try for another "X" and solution pair until 
  166. it reaches the end of the solution buffer... at which point it is, like pattern 
  167. sqp1, doomed to failure.
  168.  
  169. >      lp = fence breakx("X") ("X" len(4) $ n1) $ xn1 ("x" len(4) $ n2) $ xn2
  170. > +       *eq($xn1,1) *eq($xn2,1) *?(output = n1 "," n2) fail
  171.  
  172. Now that the patterns are built, it mostly remains just to use them.  But first 
  173. we have to build the string sqs containing all the squares.  The following 
  174. statement uses an alternative evaluation expression... the first subexpression 
  175. succeeds for values of sq in the range to produce four-digit squares, appending 
  176. those to the string sqs separated by "x" characters.  The success of that 
  177. subexpression causes the GOTO to repeat the complete expression until the first 
  178. subexpression fails (after sq exceeds 99) at which point the next two 
  179. alternatives are tried in turn.  The first one finds all the solution pairs 
  180. using pattern sqp1 (as described above), builds the solbuf solution buffer, and 
  181. ultimately fails which forces the final subexpression to be tried.  That final 
  182. subexpression does a pattern match in the solution buffer to find and print all 
  183. solution pairs meeting the "both squares unique" rule.  Ultimately, it too fails 
  184. which causes the program to end.
  185.  
  186. > sqsfill    sqs = ((sqs "x" (((sq = sq + 1) le(sq,99)) * sq)),
  187. > +     (sqs ? sqp1), (solbuf ? lp))            :s(sqsfill)
  188. > end
  189.  
  190.  
  191. > Here's mine in standard SNOBOL4:
  192.  
  193. [quote]
  194.  
  195.     &fullscan = 1
  196.     nn = 31
  197. AGAIN    nn = lt(nn, 99) nn + 1 :F(DONE)
  198.     (nn * nn) len(1) $ v len(1) $ i len(1) $ e len(1) $ r fence
  199. +        *ne(v, i) *ne(v, e) *ne(v, r) *ne(i, e) *ne(i, r) *ne(e, r)
  200. +        :F(AGAIN)
  201.     mm = 31
  202. AGAIN2    mm = lt(mm, 99) mm + 1 :F(AGAIN)
  203.     (mm * mm) len(1) $ n e len(1) $ u *n fence
  204. +        *ne(n, v) *ne(n, i) *ne(n, e) *ne(n, r) *ne(n, u)
  205. +        *ne(e, u) *ne(u, v) *ne(u, i) *ne(u, r)
  206. +        :F(AGAIN2)
  207.     $(v i e r) = $(v i e r) + 1
  208.     $(n e u n) = $(n e u n) + 1
  209.     result = result '|' v i e r ':' n e u n :(AGAIN2)
  210. DONE    result '|' (len(4) $ vier ':' len(4) $ neun) . output
  211. +        *eq($vier, 1) *eq($neun, 1)
  212. END
  213.  
  214.  
  215. maskull:Snobol>snobol4 test49.sno
  216. The Macro Implementation of SNOBOL4 in C (C-MAINBOL) Version 0.99.4
  217.     by Philip L. Budne, August 11, 1997
  218. SNOBOL4 (Version 3.11, May 19, 1975)
  219.     Bell Telephone Laboratories, Incorporated
  220.  
  221. No errors detected in source program
  222.  
  223. 6241:9409
  224. Normal termination at level 0
  225. test49.sno:16: Last statement executed was 11
  226.  
  227. [end quote]
  228.  
  229. Certainly a reasonable solution, in any case!!  Not bad at all.
  230.  
  231. It's been interesting over on the Icon list to see how they've been handling 
  232. this one using Icon and MUMPS.  One of the fairly neat solutions observes that 
  233. they can use map() (like S*BOL's REPLACE()) to do all the testing in just two 
  234. operations... of course, those might be slower operations.  It would be 
  235. interesting to benchmark it both ways.  There has also been some use of 
  236. character set variables which neatly determines (for example) for the "vier" 
  237. term that all four digits are unique.
  238.  
  239.  
  240.  
  241. <----  End Forwarded Message  ---->
  242.  
  243. Gordon Peterson                  http://personal.terabites.com/
  244. Support the Anti-SPAM Amendment!  Join at http://www.cauce.org/
  245. 12/19/98: Partisan Republicans scornfully ignore the voters they "represent".
  246. 12/09/00: the date the Republican Party took down democracy in America.
  247.  
  248.